<?php
/**
 * KePHP, Keep PHP easy!
 *
 * @license   https://opensource.org/licenses/MIT
 * @copyright Copyright 2015-2018 KePHP Authors All Rights Reserved
 * @link      http://kephp.com/utils ( https://git.oschina.net/kephp/kephp-utils )
 * @author    曾建凯 <janpoem@163.com>
 */

namespace Ke\TestUtils;

/**
 * 注解辅助测试工具（Trait）
 *
 * @package Ke\TestUtils
 */
trait TestAnnotationHelperTrait
{
	abstract public function newTestMethod(string $method): TestMethod;
	
	/**
	 * 从debug trace中取出被调用时的方法注释文档中所包含的testMethod的测试方法
	 *
	 * @param int $index
	 * @return array
	 */
	protected function getTestMethodsFromDebugTrace(int $index)
	{
		if ($index <= 0)
			$index = 0;
		
		$methods = [];
		$traces = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $index + 1);
		
		if (!empty($traces[$index])) {
			$debug = $traces[$index];
			$doc = '';
			
			try {
				switch ($debug['type'] ?? '') {
					case '->' :
						$refMethod = new \ReflectionMethod($debug['object'], $debug['function']);
						break;
					case '::' :
						$refMethod = new \ReflectionMethod($debug['class'], $debug['function']);
						break;
					default :
						$refMethod = new \ReflectionFunction($debug['function']);
				}
				
				$doc = $refMethod->getDocComment();
			} catch (\Throwable $throwable) {
			
			}
			
			if (!empty($doc) &&
				preg_match_all('#\@testMethod[\t\s]+([a-zA-Z][a-zA-Z0-9_]+)#', $doc, $matches, PREG_SET_ORDER)) {
				foreach ($matches as $match) {
					$methods[] = $this->newTestMethod($match[1]);
					// $key = mb_strtolower($match[1]);
					// if (!isset($methods[$key])) {
					// 	$methods[$key] = $this->newTestMethod($match[1]);
					// }
				}
			}
		}
		
		return array_values($methods);
	}
	
	/**
	 * 获得调用该方法时的方法注解测试方法，如果包含多个方法，将返回一个数组
	 *
	 * 目前只是一个简单实现，请勿过度基于此方法进行封装，仅仅只为了简化单元测试的书写
	 *
	 * @return array
	 */
	public function getAnnotationTestMethods(): array
	{
		return $this->getTestMethodsFromDebugTrace(2);
	}
	
	/**
	 * 获取调用该方法时注解中的最后一个测试方法实例
	 *
	 * 目前只是一个简单实现，请勿过度基于此方法进行封装，仅仅只为了简化单元测试的书写
	 *
	 * @return TestMethod
	 * @throws TestMethodException
	 */
	public function getLastAnnotationTestMethod(): TestMethod
	{
		$methods = $this->getTestMethodsFromDebugTrace(2);
		if (empty($methods))
			throw new TestMethodException('Test methods is empty!');
		return $methods[count($methods) - 1];
	}
}